package com.haiyou.data.common.accessor.redis.redisson.bucket;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.redisson.api.RList;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.haiyou.common.entity.Entity;
import com.haiyou.common.util.collect.ArrayUtils;
import com.haiyou.common.util.reflect.AnnotationFinder;
import com.haiyou.data.common.accessor.annotation.SizePolicy;
import com.haiyou.data.common.cache.redis.RKeys;
import com.haiyou.data.common.persister.mysql.help.DatabaseHelper;
import com.haiyou.data.common.persister.mysql.help.RollTableQueryHelper;
import com.haiyou.data.common.persister.mysql.annotation.Table.RollPolicy;

/**
 * 分布式映射+分布式有序对象桶容器，按插入顺序，适合用在经常需要调用getAll函数，元素缓存存活时间较短的排行榜
 * 通常用于日志类不断增长的数据，保留一部分最新热点
 * 
 * @author Administrator
 *
 * @param <K>
 * @param <V>
 */
public abstract class LinkedBucketAccessor<V extends Entity<String>> extends RedissonBucketAccessor<V> {

	private static final String SCOREDSORTED_SUFFIX = "ListBucketView";

	protected RList<String> list;// 排行榜的值只存key
	protected int greed_length;

	@Override
	public void init() {
		super.init();

		SizePolicy sizePolicy = AnnotationFinder.findAnnotation(getClass(), SizePolicy.class);
		if (sizePolicy == null) {
			greed_length = 1000;
		} else {
			greed_length = sizePolicy.maximumSize();
		}
		greed_length = Math.max(greed_length, 1);
		greed_length = Math.min(greed_length, 100000);

		list = redissonClient.getList(RKeys.createRKey(RList.class, keyPrefix, SCOREDSORTED_SUFFIX));
	}

	/**
	 * 返回 index1 和 index2 之间的所有条目
	 * 
	 * @param index1
	 * @param index2
	 * @return
	 */
	public List<V> subList(int index1, int index2) {
		index1 = Math.min(index1, index2);
		index2 = Math.max(index1, index2);
		if (index1 == index2)
			return Collections.emptyList();
		if (index2 <= list.size()) {
			return getSubListFromRList(index1, Math.min(index2, list.size()));
		}
		if (index1 >= list.size()) {
			return getSubListFromMySql(index1, index2);
		}
		if (index1 < list.size() && index2 > list.size()) {
			List<V> result = Lists.newArrayListWithCapacity(index2 - index1);
			List<V> redisList = getSubListFromRList(index1, list.size());
			List<V> mySqlList = getSubListFromMySql(list.size(), index2);
			result.addAll(redisList);
			result.addAll(mySqlList);
			return result;
		}
		return Collections.emptyList();
	}

	private List<V> getSubListFromRList(int index1, int index2) {
		return ArrayUtils.reversed2(getAll(Lists.newArrayList(list.subList(list.size() - index2, list.size() - index1)))
				.values().stream().collect(Collectors.toList()));
	}

	private List<V> getSubListFromMySql(int index1, int index2) {
		List<V> result = null;
		try {
			if (persister.getTableMeta().getRollPolicy() != RollPolicy.NONE) {
				return result = RollTableQueryHelper.queryByLimits(valueClass, index1, index2, true);
			} else {
				return result = persister.queryByLimits(DatabaseHelper.getOnlyReallyName(persister.getDatabaseMeta()),
						persister.getTableMeta().getSimpleName(), index1, index2, true);
			}
		} finally {
			if (result != null && !result.isEmpty()) {
				for (int i = 0; i < result.size(); i++) {
					if (list.size() < greed_length)
						list.add(result.get(i).getId());
				}
			}
		}
	}

	private void reSize() {
		while (list.size() > greed_length) {
			list.removeAsync(0);
		}
	}

	@Override
	public V put(String key, V value) {
		// list.removeAsync(key);
		list.addAsync(key);
		reSize();
		return super.put(key, value);
	}

	@Override
	public void putAll(Map<? extends String, ? extends V> m) {
		// list.removeAllAsync(m.keySet());
		list.addAllAsync(m.keySet());
		reSize();
		super.putAll(m);
	}

	@Override
	public V remove(Object key) {
		list.removeAsync(key);
		return super.remove(key);
	}

	@Override
	public void removeAll(Iterable<? extends String> keys) {
		list.removeAllAsync(Sets.newHashSet(keys));
		super.removeAll(keys);
	}

}
